package d.n.d.service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import d.n.d.constant.Constant;
import d.n.d.dao.TransactionDao;
import d.n.d.domain.Account;
import d.n.d.domain.RespJSON;
import d.n.d.domain.Transaction;
import d.n.d.domain.User;

/**
 * 
 * @author D.N.D
 * 
 */
@Service
public class TransactionServiceImpl implements TransactionService {

	@Autowired
	TransactionDao transactionDao;

	@Autowired
	AccountService accountService;
	
//	private TransactionService transactionService;

	private String error = null;
	private String message = null;
	private String validation = null;

	/**
	 * 
	 * Author: DucLV 
	 * Implement method of interface.
	 */
	public RespJSON confirmInfoMoneyAdd(String accountNumber,
			String moneyTransaction) {

		Account account = accountService
				.findAccountInfoByAccountInfoNumber(accountNumber);
		account.getIdCardNumber().setAccounts(null);

		Double moneyRemain = null;

		String verify = verifyAddFund(moneyTransaction);
		if (verify.equals("")) {
			message = Constant.TRANSACTION_VALID;
			error = null;
			moneyRemain = account.getBalance()
					+ Double.parseDouble((String) moneyTransaction);
		} else {
			error = verify;
			message = null;
		}

		RespJSON respJSON = new RespJSON(moneyTransaction, account,
				moneyRemain, message, error);

		return respJSON;
	}

	/**
	 * 
	 * Author: DucLV
	 * Implement method of interface.
	 */
	public RespJSON executeMoneyAdd(String accountNumber,
			double moneyTransaction, User userSupport) {
		
		Account account = accountService
				.findAccountInfoByAccountInfoNumber(accountNumber);
		account.getIdCardNumber().setAccounts(null);

		String verify = verifyAddFund(Double.toString(moneyTransaction));
		
		if (verify.equals("")) {
			// create add funds transaction in database
			boolean doTransaction = createAddFundsTransaction(moneyTransaction, account,
							userSupport);
			
			if (doTransaction == false) {
				error = Constant.ERROR_TRANSACTION_ADD_FUND;
				message = null;
			} else {
				error = null;
				message = Constant.TRANSACTION_SUCCESS;
			}

		} else {
			error = verify;
			message = null;
		}

		RespJSON respJSON = new RespJSON(message, error, validation);
		
		return respJSON;
	}

	/**
	 * 
	 * Author: DucLV
	 * Implement method of interface.
	 */
	public RespJSON confirmInfoApproveTransaction(String transactionId) {
		
		String validation = verifyTransaction(transactionId);
		if (validation.equals("")) {
			message = Constant.TRANSACTION_VALID;
			error = null;
		} else {
			error = Constant.TRANSACTION_INVALID;
			message = null;
		}
		Transaction transaction = findTransactionById(Integer.parseInt(transactionId));
		if (transaction.getSendAccount() != null) {
			transaction.getSendAccount().getIdCardNumber().setAccounts(null);
		}
		if (transaction.getReceiveAccount() != null) {
			transaction.getReceiveAccount().getIdCardNumber().setAccounts(null);
		}
		if (transaction.getUserSupport() != null) {
			transaction.getUserSupport().setTransactionUserAdmin(null);
			transaction.getUserSupport().setTransactionUserSupport(null);
		}

		RespJSON respJSON = new RespJSON(message, error, transaction,
				validation);
		
		return respJSON;
	}

	/**
	 *
	 * Author: DucLV 
	 * Implement method of interface.
	 */
	public RespJSON executeApproveTransaction(String transactionId,
			String transactionComment, User userAdmin) {
		
		if (transactionComment.length() > 30) {
			transactionComment = transactionComment.substring(0, 30);
		}
		String validation = verifyTransaction(transactionId);

		if (validation.equals("")) {
			if (approve(transactionId, userAdmin, transactionComment)) {
				message = "Transaction Id = " + transactionId + " is approved";
				error = null;
			} else {
				error = Constant.ERROR_SQL;
				message = null;
			}
		} else {
			error = Constant.TRANSACTION_INVALID;
			message = null;
		}
		Transaction transaction = findTransactionById(Integer.parseInt(transactionId));

		if (transaction.getSendAccount() != null) {
			transaction.getSendAccount().getIdCardNumber().setAccounts(null);
		}
		if (transaction.getReceiveAccount() != null) {
			transaction.getReceiveAccount().getIdCardNumber().setAccounts(null);
		}
		if (transaction.getUserSupport() != null) {
			transaction.getUserSupport().setTransactionUserAdmin(null);
			transaction.getUserSupport().setTransactionUserSupport(null);
		}

		if (transaction.getUserAdmin() != null) {
			transaction.getUserAdmin().setTransactionUserAdmin(null);
			transaction.getUserAdmin().setTransactionUserSupport(null);
		}
		RespJSON respJSON = new RespJSON(message, error, transaction,
				validation);
		
		return respJSON;
	}
	
	public List<Transaction> getTransactionListByAccountId(String userId) {
		return transactionDao.getTransactionListByAccountId(userId);
	}

	public Transaction findTransactionById(int transactionId) {
		return transactionDao.findTransactionById(transactionId);
	}

	public List<Transaction> getTransactionListByCustomer(Date from, Date to,
			String userId) {
		return transactionDao.getTransactionListByCustomer(from, to, userId);
	}

	public List<Transaction> getTransactionListPending() {
		return transactionDao.getTransactionListPending();
	}

	public Transaction findTransactionByStringId(String transactionId) {
		return transactionDao.findTransactionByStringId(transactionId);
	}

	public boolean updateTransaction(Transaction transaction) {
		return transactionDao.updateTransaction(transaction);
	}

	public boolean update(Account account, Transaction transaction) {
		return transactionDao.update(account, transaction);
	}

	public boolean update(Account sendAccount, Account receiveAccount,
			Transaction transaction) {
		return transactionDao.update(sendAccount, receiveAccount, transaction);
	}

	public String verifyAddFund(String moneyTransaction) {

		Double money;
		try {
			money = Double.parseDouble((String) moneyTransaction);
		} catch (Exception e) {
			return Constant.ERROR_PARSE_DOUBLE;
		}

		if (money < 1 || money > 2000000000) {
			return Constant.ERROR_ADD_FUND_AMOUNT;
		}
		return "";

	}

	public boolean verifyWithdraw(String moneyTransactionWithdraw,
			double balance) {

		// String strCheck = "valid";
		if (balance < Double.parseDouble((String) moneyTransactionWithdraw)) {

			return false;
		}

		return true;

	}

	/**
	 * @return String
	 */
	public String verifyTransaction(String transactionId) {

		try {
			Integer.parseInt(transactionId);
		} catch (Exception e) {
			return Constant.ERROR_PARSE_INT;
		}

		Transaction transaction = transactionDao.findTransactionById(Integer
				.parseInt(transactionId));

		if (transaction == null) {
			return Constant.ERROR_TRANSACTION_NOT_FOUND;
		}

		StringBuffer error = new StringBuffer();
		String transactionState = transaction.getTransactionStatus().trim();
		String transactionType = transaction.getTransactionType().trim();

		if (!transactionType.equals(Constant.TRANSACTION_TYPE_ADDFUNDS)
				&& !transactionType.equals(Constant.TRANSACTION_TYPE_WITHDRAW)
				&& !transactionType.equals(Constant.TRANSACTION_TYPE_TRANSFER)) {
			error.append(Constant.ERROR_TRANSACTION_TYPE);
		}

		if (!transactionState.equals(Constant.TRANSACTION_STATE_PENDING)
				&& !transactionState
						.equals(Constant.TRANSACTION_STATE_APPROVED)
				&& !transactionState
						.equals(Constant.TRANSACTION_STATE_REJECTED)) {
			error.append(Constant.ERROR_TRANSACTION_STATE);
		}

		if (transactionState.equals(Constant.TRANSACTION_STATE_APPROVED)) {
			error.append(Constant.ERROR_TRANSACTION_APPROVED);
		}

		if (transactionState.equals(Constant.TRANSACTION_STATE_REJECTED)) {
			error.append(Constant.ERROR_TRANSACTION_REJECTED);
		}

		if (transaction.getUserSupport() != null) {
			if (!Constant.USER_SUPPORT.equals(transaction.getUserSupport()
					.getUserRole())) {
				error.append(Constant.ERROR_USER_SUPPORT_NOT_ROLE);
			}
		} else {
			error.append(Constant.ERROR_USER_SUPPORT_NOT_FOUND);
		}

		if (transaction.getBeginTime().after(new Date())) {
			error.append(Constant.ERROR_DATE_TIME_CREATED);
		}

		if (transaction.getChangeAmount() < 1) {
			error.append(Constant.ERROR_AMOUNT_TRANSFER);
		}
		if (transaction.getSendAccount() != null) {
			if (transactionType.equals(Constant.TRANSACTION_TYPE_ADDFUNDS)) {
				// Verify add funds account active
				if (!Constant.ACCOUNT_STATE_ACTIVE.equals(transaction
						.getSendAccount().getAccountState())) {
					error.append(Constant.ERROR_ADDFUNDS_ACCOUNT_NOT_ACTIVE);
				}
			} else if (transactionType
					.equals(Constant.TRANSACTION_TYPE_WITHDRAW)) {
				// Verify withdraw
				// Verify withdraw account balance
				if (transaction.getSendAccount().getBalance() < transaction
						.getChangeAmount()) {
					error.append(Constant.ERROR_BALANCE_FOR_WITHDRAW);
				}
				// Verify withdraw account active
				if (!Constant.ACCOUNT_STATE_ACTIVE.equals(transaction
						.getSendAccount().getAccountState())) {
					error.append(Constant.ERROR_WITHDRAW_ACCOUNT_NOT_ACTIVE);
				}
			} // Verify transfer
			else if (transactionType.equals(Constant.TRANSACTION_TYPE_TRANSFER)) {
				// Verify sender account balance
				if (transaction.getSendAccount().getBalance() < transaction
						.getChangeAmount()) {
					error.append(Constant.ERROR_BALANCE_FOR_TRANSFER);
				}
				// Verify sender account active
				if (!Constant.ACCOUNT_STATE_ACTIVE.equals(transaction
						.getSendAccount().getAccountState())) {
					error.append(Constant.ERROR_SENDER_ACCOUNT_NOT_ACTIVE);
				}
				// Verify receiver founded
				if (transaction.getReceiveAccount() == null) {
					error.append(Constant.ERROR_RECEIVER_ACCOUNT_NOT_FOUND);
				} else {
					// Verify sender account active
					if (!Constant.ACCOUNT_STATE_ACTIVE.equals(transaction
							.getReceiveAccount().getAccountState())) {
						error.append(Constant.ERROR_RECEIVER_ACCOUNT_NOT_ACTIVE);
					}

					// Verify same sender and receiver
					if (transaction.getSendAccount().getAccountNumber() == transaction
							.getReceiveAccount().getAccountNumber()) {
						error.append(Constant.ERROR_SAME_ACCOUNT_TRANSFER);
					}
				}
			}
		} else {
			error.append(Constant.ERROR_SENDER_ACCOUNT_NOT_FOUND);
		}
		return error.toString();
	}

	/**
	 * @return boolean
	 */
	public boolean approve(String transactionId, User userAmin, String comment) {
		boolean check = false;
		Transaction trans = transactionDao.findTransactionById(Integer
				.parseInt(transactionId));
		if (trans.getTransactionType().equals(
				Constant.TRANSACTION_TYPE_ADDFUNDS)) {
			check = approveAddFunds(trans, userAmin, comment);
		} else if (trans.getTransactionType().equals(
				Constant.TRANSACTION_TYPE_WITHDRAW)) {
			check = approveWithdraw(trans, userAmin, comment);
		} else if (trans.getTransactionType().equals(
				Constant.TRANSACTION_TYPE_TRANSFER)) {
			check = approveTransfer(trans, userAmin, comment);
		}
		return check;
	}

	/**
	 * @return boolean
	 */
	public boolean reject(String transactionId, User userAmin, String comment) {
		try {
			Transaction trans = transactionDao.findTransactionById(Integer
					.parseInt(transactionId));
			trans.setEndTime(new Date());
			trans.setComment(comment);
			trans.setUserAdmin(userAmin);
			trans.setTransactionStatus(Constant.TRANSACTION_STATE_REJECTED);
			transactionDao.updateTransaction(trans);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * @param transaction
	 * @param userAmin
	 * @param comment
	 * @return boolean
	 */
	public boolean approveAddFunds(Transaction transaction, User userAmin,
			String comment) {
		try {
			double currentBalance = transaction.getSendAccount().getBalance();
			transaction.getSendAccount().setBalance(
					currentBalance + transaction.getChangeAmount());
			transaction.setEndTime(new Date());
			transaction.setComment(comment);
			transaction.setUserAdmin(userAmin);
			transaction
					.setTransactionStatus(Constant.TRANSACTION_STATE_APPROVED);
			transactionDao.update(transaction.getSendAccount(), transaction);

		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param transaction
	 * @param userAmin
	 * @param comment
	 * @return boolean
	 */
	public boolean approveWithdraw(Transaction transaction, User userAmin,
			String comment) {
		try {
			double currentBalance = transaction.getSendAccount().getBalance();
			transaction.getSendAccount().setBalance(
					currentBalance - transaction.getChangeAmount());
			transaction.setEndTime(new Date());
			transaction.setComment(comment);
			transaction.setUserAdmin(userAmin);
			transaction
					.setTransactionStatus(Constant.TRANSACTION_STATE_APPROVED);
			transactionDao.update(transaction.getSendAccount(), transaction);

		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param transaction
	 * @param userAmin
	 * @param comment
	 * @return boolean
	 */
	public boolean approveTransfer(Transaction transaction, User userAmin,
			String comment) {

		try {
			double currentBalanceSender = transaction.getSendAccount()
					.getBalance();
			transaction.getSendAccount().setBalance(
					currentBalanceSender - transaction.getChangeAmount());

			double currentBalanceReceiver = transaction.getReceiveAccount()
					.getBalance();
			transaction.getReceiveAccount().setBalance(
					currentBalanceReceiver + transaction.getChangeAmount());
			transaction.setEndTime(new Date());
			transaction.setComment(comment);
			transaction.setUserAdmin(userAmin);
			transaction
					.setTransactionStatus(Constant.TRANSACTION_STATE_APPROVED);
			transactionDao.update(transaction.getSendAccount(),
					transaction.getReceiveAccount(), transaction);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * @return boolean
	 */
	public boolean createAddFundsTransaction(double doubleAmount,
			Account sendAccount, User userSupport) {

		// crate transaction
		Transaction transferTransaction = new Transaction(new Date(),
				doubleAmount, Constant.TRANSACTION_TYPE_ADDFUNDS,
				Constant.TRANSACTION_STATE_PENDING, sendAccount, null, null,
				userSupport);

		// insert transaction in database
		try {
			transactionDao.insertNewTransaction(transferTransaction);
		} catch (Exception e) {
			return false;
		}

		return true;
	}

	/**
	 * @return boolean
	 */
	public boolean createWithdrawTransaction(double doubleAmount,
			Account tempAccount, User userSupport) {

		// create transaction
		Transaction transferTransaction = new Transaction(new Date(),
				doubleAmount, Constant.TRANSACTION_TYPE_WITHDRAW,
				Constant.TRANSACTION_STATE_PENDING, tempAccount, null, null,
				userSupport);

		// insert transaction in database
		try {
			transactionDao.insertNewTransaction(transferTransaction);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * <br>
	 * Author: DucDS </br>
	 * 
	 * @return boolean
	 */
	public boolean createTransferTransaction(String amountTransfer,
			String senderAccountNumber, String receiverAccountNumber,
			User userSupport) {

		try {
			Account senderAccount = accountService
					.findAccountInfoByAccountInfoNumber(senderAccountNumber);
			Account receiverAccount = accountService
					.findAccountInfoByAccountInfoNumber(receiverAccountNumber);
			Transaction transferTransaction = new Transaction(new Date(),
					Double.parseDouble(amountTransfer),
					Constant.TRANSACTION_TYPE_TRANSFER,
					Constant.TRANSACTION_STATE_PENDING, senderAccount,
					receiverAccount, userSupport);
			transactionDao.insertNewTransaction(transferTransaction);
		} catch (Exception e) {
			return false;
		}

		return true;
	}

	/**
	 * <br>
	 * Author: DucDS </br> Implement method of interface.
	 */
	public RespJSON confirmInfoMoneyTransfer(String pstrAccountNumber,
			String pstrMoneyTransfer, String pstrReceiverAccount) {

		String strCheck = "";
		Double moneyRemain = 0.0;

		Account account = accountService
				.findAccountInfoByAccountInfoNumber(pstrAccountNumber);
		account.getIdCardNumber().setAccounts(null);
		Account receiverAccount = accountService
				.findAccountInfoByAccountInfoNumber(pstrReceiverAccount);
		ArrayList<Account> lstAccount = new ArrayList<Account>();
		if (pstrAccountNumber.equals(pstrReceiverAccount)) {
			strCheck = "invalidUser";
		} else if (account.getBalance() < Double
				.parseDouble((String) pstrMoneyTransfer)) {
			strCheck = "invalidMoney";
		} else if (receiverAccount == null) {
			strCheck = "invalidReceiver";
		} else if (!"active".equals(receiverAccount.getAccountState())) {
			strCheck = "disable";
		} else {
			moneyRemain = account.getBalance()
					- Double.parseDouble((String) pstrMoneyTransfer);
			lstAccount.add(account);
			lstAccount.add(receiverAccount);
			lstAccount.get(1).getIdCardNumber().setAccounts(null);
		}
		RespJSON respJSON = new RespJSON(lstAccount, pstrMoneyTransfer,
				moneyRemain, strCheck);
		return respJSON;
	}

	/**
	 * <br>
	 * Author: DucDS </br> Implement method of interface.
	 */
	public RespJSON executeMoneyTransfer(String pstrAccountNumber,
			String pstrMoneyTransfer, String pstrReceiverAccount, User pUser) {

		String strError;
		String strMessage;

		Account account = accountService
				.findAccountInfoByAccountInfoNumber(pstrAccountNumber);
		if (account.getBalance() < Double
				.parseDouble((String) pstrMoneyTransfer)) {
			strError = "Transaction failed because balance has been changed! Balance now : "
					+ account.getBalance() + "$";
			strMessage = null;
		} else {
			createTransferTransaction(pstrMoneyTransfer, pstrAccountNumber,
					pstrReceiverAccount, pUser);
			strError = null;
			strMessage = Constant.TRANSACTION_SUCCESS;
		}
		RespJSON respJSON = new RespJSON(strMessage, strError, null);
		return respJSON;
	}

	/**
	 * <br>
	 * Author: DucDS </br> <br>
	 * Implement method of interface.</br>
	 */
	public boolean isValidDate(String pstrFromDate, String pstrToDate) {

		if (!"".equals(pstrFromDate) && !"".equals(pstrToDate)) {
			try {
				boolean isCheck = new SimpleDateFormat("yyyy-MM-dd").parse(
						pstrFromDate).before(
						new SimpleDateFormat("yyyy-MM-dd").parse(pstrToDate));
				return isCheck;
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		return true;
	}

	/**
	 * <br>
	 * Author: DucDS </br> <br>
	 * Implement method of interface.</br>
	 */
	public String searchTransactionByIdCard(Model pModel, String pstrIdCard) {

		List<Account> lstAccount = accountService
				.getListAccountByIdCardActive(pstrIdCard);
		if (lstAccount.size() > 0) {
			pModel.addAttribute("viewAccounts", lstAccount);
			return "viewTransaction";
		} else {
			pModel.addAttribute("error", "ID Card is not exist!");
			return "viewTransaction";
		}
	}

	/**
	 * <br>
	 * Author: DucDS </br> <br>
	 * Implement method of interface.</br>
	 */
	public String viewTransactionByAccountNumber(Model pModel,
			String pstrIdCardNumber, String pstrAccountNumber, Date fromDate,
			Date toDate, String pstrFromDate, String pstrToDate) {

		List<Transaction> viewTransactions = null;
		List<Account> lstAccount = accountService
				.getListAccountByIdCardActive(pstrIdCardNumber);
		if (pstrAccountNumber != null && fromDate == null && toDate == null) {
			viewTransactions = getTransactionListByAccountId(pstrAccountNumber);
		} else if (pstrAccountNumber != null) {
			viewTransactions = getTransactionListByCustomer(fromDate, toDate,
					pstrAccountNumber);
		}
		pModel.addAttribute("viewAccounts", lstAccount);
		if (viewTransactions.size() <= 0) {
			// show message
			pModel.addAttribute("messTrans", "Account has no transaction !");
			return "viewTransaction";
		}
		if ("".equals(pstrFromDate) && "".equals(pstrToDate)) {
			pModel.addAttribute("messTrans", "Account " + pstrAccountNumber
					+ " has all " + viewTransactions.size() + " Transaction");
		} else if ("".equals(pstrFromDate)) {
			pModel.addAttribute("messTrans", "Account " + pstrAccountNumber
					+ " has " + viewTransactions.size() + " Transaction to "
					+ pstrToDate);
		} else if ("".equals(pstrToDate)) {
			pModel.addAttribute("messTrans", "Account " + pstrAccountNumber
					+ " has " + viewTransactions.size() + " Transaction from "
					+ pstrFromDate + " to now");
		} else {
			pModel.addAttribute("messTrans", "Account " + pstrAccountNumber
					+ " has " + viewTransactions.size() + " Transaction from "
					+ pstrFromDate + " to " + pstrToDate);
		}
		pModel.addAttribute("viewTransactions", viewTransactions);
		return "viewTransaction";
	}



}
